﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Management.Automation;
using System.Net;
using System.Text;
using Microsoft.PowerShell.Commands;
using Microsoft.SharePoint.Client;
using HtmlAgilityPack;

namespace SharePointOnlineCmdlets
{
    /// <summary>
    /// Creates a List based on a list template inside a Parent Web
    /// </summary>
    [Cmdlet(VerbsCommon.New, "SPOList")]
    public class NewSPOList : Cmdlet
    {
        [Parameter(Mandatory = true,ValueFromPipeline = true)]
        public Web Web { get; set; }

        [Parameter(Mandatory = true)]
        public string Title { get; set; }

        [Parameter]
        public string Description { get; set; }

        [Parameter(Mandatory = true)]
        public string ListTemplate { get; set; }

        [Parameter(Mandatory = true)]
        public string Url { get; set; }

        [Parameter]
        public string FeatureId { get; set; }

        protected override void ProcessRecord()
        {
            var Context = Web.Context;
            var lists = Web.Lists;
            Context.Load(lists);
            Context.ExecuteQuery();
            ListCreationInformation list;

            if (!String.IsNullOrEmpty(FeatureId))
            {
                list = new ListCreationInformation()
                    {
                        Title = Title,
                        Url = Url,
                        Description = Description,
                        TemplateType = SPHelpers.GetListTemplate(ListTemplate),
                        TemplateFeatureId = Guid.Parse(FeatureId)
                    };
            }
            else
            {
                list = new ListCreationInformation()
                {
                    Title = Title,
                    Url = Url,
                    Description = Description,
                    TemplateType = SPHelpers.GetListTemplate(ListTemplate)
                };
            }
            var newList = lists.Add(list); 
            Context.ExecuteQuery();
            WriteObject(newList);
        }
    }

    /// <summary>
    /// Gets all lists in a web
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "SPOLists")]
    public class GetSPOLists : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public Web Web { get; set; }

        protected override void ProcessRecord()
        {
            var Context = Web.Context;
            var lists = Web.Lists;
            Context.Load(lists);
            Context.ExecuteQuery();
            WriteObject(lists);
        }
    }

    /// <summary>
    /// Gets a list by title or id
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "SPOList")]
    public class GetSPOList : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public Web Web { get; set; }

        [Parameter]
        public string Id { get; set; }

        [Parameter]
        public string Title { get; set; }

        protected override void ProcessRecord()
        {
            var Context = Web.Context;
            var list = String.IsNullOrEmpty(Id) ? Web.Lists.GetByTitle(Title) : Web.Lists.GetById(Guid.Parse(Id));
            Context.Load(list);
            Context.ExecuteQuery();
            WriteObject(list);
        }
    }

    /// <summary>
    /// Removes a List inside a Parent Web based on the title of the list.
    /// </summary>
    [Cmdlet(VerbsCommon.Remove, "SPOList")]
    public class RemoveSPOList : Cmdlet
    {
        [Parameter(Mandatory = true,ValueFromPipeline = true)]
        public List List { get; set; }

        protected override void ProcessRecord()
        {
            var Context = List.Context;
            List.DeleteObject();
            Context.ExecuteQuery();
        }
    }

    /// <summary>
    /// Removes a List inside a Parent Web based on the title of the list.
    /// </summary>
    [Cmdlet(VerbsCommon.Add, "SPOListView")]
    public class AddSPOListView : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public List List { get; set; }

        [Parameter(Mandatory = true)]
        public String Title { get; set; }

        [Parameter(Mandatory = true)]
        public String Fields { get; set; }

        [Parameter(Mandatory = true)]
        public uint RowLimit { get; set; }

        [Parameter]
        public String Type { get; set; }

        [Parameter]
        public String Query { get; set; }

        [Parameter]
        public SwitchParameter Paged { get; set; }

        [Parameter]
        public SwitchParameter Personal { get; set; }

        [Parameter]
        public SwitchParameter Default { get; set; }

        protected override void ProcessRecord()
        {
            var Context = List.Context;
            Type = Type ?? "Html";
            Query = Query ?? String.Empty;
            var fields = Fields.Split(',').ToArray();
            ViewType viewtype;
            if (ViewType.TryParse(Type, out viewtype))
            {
                List.Views.Add(new ViewCreationInformation()
                {
                    Paged = Paged.IsPresent,
                    PersonalView = Personal.IsPresent,
                    Query = Query,
                    RowLimit = RowLimit,
                    SetAsDefaultView = Default.IsPresent,
                    Title = Title,
                    ViewTypeKind = viewtype,
                    ViewFields = fields
                });
                List.Update();
                Context.Load(List);
                Context.ExecuteQuery();
            }
            WriteObject(List);
        }
    }

    /// <summary>
    /// Removes a ListView from a list by ListView Id or Title.
    /// </summary>
    [Cmdlet(VerbsCommon.Remove, "SPOListView")]
    public class RemoveSPOListView : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public List List { get; set; }

        [Parameter]
        public String Id { get; set; }

        [Parameter]
        public String Title { get; set; }

        protected override void ProcessRecord()
        {
            var Context = List.Context;
            View view;
            if (!String.IsNullOrEmpty(Id))
            {
                view = List.Views.GetById(Guid.Parse(Id));
            }
            else
            {
                view = List.Views.GetByTitle(Title);
            }
            Context.Load(view);
            Context.ExecuteQuery();
            view.DeleteObject();
            List.Update();
            Context.Load(List);
            Context.ExecuteQuery();
            WriteObject(List);
        }
    }

    /// <summary>
    /// Add existing content type to a list by title
    /// </summary>
    [Cmdlet(VerbsCommon.Add, "SPOListCT")]
    public class AddSPOListCT : Cmdlet
    {
        [Parameter(Mandatory = true,ValueFromPipeline = true)]
        public Web Web { get; set; }

        //List title
        [Parameter(Mandatory = true)]
        public string Title { get; set; }

        //Content Type Names e.g. 'Test Content Page;New Content Page'
        [Parameter(Mandatory = true)]
        public string ContentType { get; set; }

        protected override void ProcessRecord()
        {
            var Context = Web.Context;
            var lists = Web.Lists;
            var cts = Web.AvailableContentTypes;
            Context.Load(lists);
            Context.Load(cts);
            Context.ExecuteQuery();

            //Get specified list by title
            var list = lists.GetByTitle(Title);
            if (list == null)
            {
                throw new Exception("No list with the name of " + Title + " was found!");
            }
            Context.Load(list);
            Context.ExecuteQuery();

            //Split Content Type Lists
            var ctypesList = ContentType.Split(';');

            //Enable Content Types
            list.ContentTypesEnabled = true;

            //Add each content type
            foreach (var ctype in ctypesList)
            {
                //Get Content Type by Id
                var contentType = cts.SingleOrDefault(x => x.Name == ctype);

                //Update to include existing content type
                if (ctype != null)
                {
                    list.ContentTypes.AddExistingContentType(contentType);
                }
            }

            list.Update();
            Context.ExecuteQuery();

            WriteObject(list);
        }

    }

    /// <summary>
    /// Adds items to a list from a csv file. You must provide a csv with a header line with the internal names of the fields to be filled in.
    /// Semicolons will be replaced by newlines.
    /// </summary>
    [Cmdlet(VerbsData.Import, "SPOListItemsFromCSV")]
    public class ImportSPOListItemsFromCSV : Cmdlet
    {
        [Parameter(Mandatory = true,ValueFromPipeline = true)]
        public Web Web { get; set; }

        [Parameter(Mandatory = true)]
        public string List { get; set; }

        [Parameter(Mandatory = true)]
        public string File { get; set; }

        protected override void ProcessRecord()
        {
            var ctx = (ClientContext)Web.Context;
            var list = ctx.Web.Lists.GetByTitle(List);
            ctx.Load(list);
            ctx.ExecuteQuery();

            var lines = System.IO.File.ReadAllLines(File);
            var fields = lines[0].Split(',');

            for (int i = 1; i < lines.Length; i++)
            {
                var item = list.AddItem(new ListItemCreationInformation());
                var values = lines[i].Split(new char[] { ',' }, StringSplitOptions.None);
                int position = 0;
                foreach (var field in fields)
                {
                    item[field] = values[position].Replace(";", "\n");
                    position++;
                }
                item.Update();
                ctx.ExecuteQuery();
            }

            base.ProcessRecord();
        }
    }

    /// <summary>
    /// Modify a field of a list item. Provide the field name, the value you want to change it to  
    /// and the server relative url of the list item
    /// </summary>
    [Cmdlet(VerbsCommon.Set, "SPOListItemField")]
    public class SetSPOListItemField : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public ClientContext Context { get; set; }

        [Parameter(Mandatory = true)]
        public string FieldName { get; set; }

        [Parameter(Mandatory = true)]
        public string FieldValue { get; set; }

        [Parameter(Mandatory = true)]
        public string ItemServerRelativeUrl { get; set; }
        

        protected override void ProcessRecord()
        {
            var ctx = Context;
            var file = ctx.Web.GetFileByServerRelativeUrl(ItemServerRelativeUrl);

            ctx.Load(file);
            ctx.ExecuteQuery();
            
            if (file.CheckOutType != CheckOutType.None)
            {
                throw new Exception("Could not modify fields, list item is checked out by another user");
            }

            file.CheckOut();
            ctx.ExecuteQuery();

            ctx.Load(file.ListItemAllFields);
            ctx.ExecuteQuery();

            var originalFieldName = file.ListItemAllFields[FieldName];

            file.ListItemAllFields[FieldName] = FieldValue;

            file.ListItemAllFields.Update();

            string updatedDate = DateTime.Now.ToString();

            file.CheckIn("updated" + FieldName + " from " + originalFieldName + " to " + FieldValue + " on " + updatedDate, CheckinType.MajorCheckIn);

            ctx.ExecuteQuery();

            base.ProcessRecord();
        }
    }

}
